package com.javen.demo;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TimeTest {

    public static class DateUtil {

        /**
         * 锁对象
         */
        private static final Object lockObj = new Object();

        /**
         * 存放不同的日期模板格式的sdf的Map
         */
        private static final Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();


        /**
         * 返回一个ThreadLocal的sdf,每个线程只会new一次sdf
         *
         * @param pattern 时间格式
         * @return SimpleDateFormat
         */
        private static SimpleDateFormat getSdf(final String pattern) {
            ThreadLocal<SimpleDateFormat> tl = sdfMap.get(pattern);

            // 此处的双重判断和同步是为了防止sdfMap这个单例被多次put重复的sdf
            if (tl == null) {
                synchronized (lockObj) {
                    tl = sdfMap.get(pattern);
                    if (tl == null) {
                        // 只有Map中还没有这个pattern的sdf才会生成新的sdf并放入map
                        System.out.println("put new sdf of pattern " + pattern + " to map");

                        // 这里是关键,使用ThreadLocal<SimpleDateFormat>替代原来直接new SimpleDateFormat
                        tl = ThreadLocal.withInitial(() -> {
                            System.out.println("thread: " + Thread.currentThread() + " init pattern: " + pattern);
                            return new SimpleDateFormat(pattern);
                        });
                        sdfMap.put(pattern, tl);
                    }
                }
            }

            return tl.get();
        }

        /**
         * 使用ThreadLocal<SimpleDateFormat>来获取SimpleDateFormat,这样每个线程只会有一个SimpleDateFormat
         * 如果新的线程中没有SimpleDateFormat，才会new一个
         *
         * @param date
         * @param pattern
         * @return
         */
        public static String format(Date date, String pattern) {
            return getSdf(pattern).format(date);
        }

        public static Date parse(String dateStr, String pattern) throws ParseException {
            return getSdf(pattern).parse(dateStr);
        }
    }


    public static void TestDateUtil() throws InterruptedException {
        Thread t1 = new Thread(() -> DateUtil.format(new Date(), "yyyy-MM-dd"));

        Thread t2 = new Thread(() -> DateUtil.format(new Date(), "yyyy-MM-dd"));

        Thread t3 = new Thread(() -> DateUtil.format(new Date(), "yyyy-MM-dd"));

        Thread t4 = new Thread(() -> {
            try {
                DateUtil.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
            } catch (ParseException e) {
                e.printStackTrace();
            }
        });

        Thread t5 = new Thread(() -> {
            try {
                DateUtil.parse("2017-06-10 12:00:01", "yyyy-MM-dd HH:mm:ss");
            } catch (ParseException e) {
                e.printStackTrace();
            }
        });


        System.out.println("单线程执行：");
        ExecutorService exec1 = Executors.newFixedThreadPool(1);
        exec1.execute(t1);
        exec1.execute(t2);
        exec1.execute(t3);
        exec1.execute(t4);
        exec1.execute(t5);
        exec1.shutdown();

        TimeUnit.SECONDS.sleep(10);

        System.out.println("双线程执行：");
        ExecutorService exec2 = Executors.newFixedThreadPool(2);
        exec2.execute(t1);
        exec2.execute(t2);
        exec2.execute(t3);
        exec2.execute(t4);
        exec2.execute(t5);
        exec2.shutdown();
    }

    public static void time() {
        /* 通过静态方法 now() 返回该类的实例 */
        //获取当前的日期时分秒
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);

        //获取当前的日期
        LocalDate now1 = LocalDate.now();
        System.out.println(now1);

        //获取当前的时分秒
        LocalTime now2 = LocalTime.now();
        System.out.println(now2);

        System.out.println("=========================================");
        /* 静态方法 of() 返回该类的实例 */
        //指定日期时分秒
        LocalDateTime localDateTime = LocalDateTime.of(2048, 11, 25, 12, 00, 30);
        System.out.println(localDateTime);

        //指定日期
        LocalDate date = LocalDate.of(2020, 12, 12);
        System.out.println(date);

        //指定时分秒
        LocalTime time = LocalTime.of(14, 20, 30);
        System.out.println(time);

        System.out.println("=========================================");

        //获取年份
        int year = now.getYear();
        System.out.println(year);

        //获取月份枚举
        //Month 枚举类，定义了十二个月份
        Month month = now.getMonth();
        System.out.println(month);

        //获取月份的数值
        int monthValue = now.getMonthValue();
        System.out.println(monthValue);

        //获取当天在本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        System.out.println(dayOfMonth);

        //获取小时
        int hour = now.getHour();
        System.out.println(hour);

        //获取分钟
        int minute = now.getMinute();
        System.out.println(minute);

        //获取秒值
        int second = now.getSecond();
        System.out.println(second);

        System.out.println("=========================================");


        //指定格式
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
        //传入格式
        String dateStr = now.format(ofPattern);
        System.out.println(dateStr);

        System.out.println("=========================================");


        //将目标LocalDateTime转换为相应的LocalDate对象
        LocalDate localDate = now.toLocalDate();
        System.out.println(localDate);

        ///将目标LocalDateTime转换为相应的LocalTime对象
        LocalTime localTime = now.toLocalTime();
        System.out.println(localTime);

        System.out.println("=========================================");

        //指定的日期
        LocalDate of = LocalDate.of(2015, 12, 12);

        //判断一个日期是否在另一个日期之前
        boolean before = of.isBefore(now1);
        System.out.println(before);

        //判断一个日期是否在另一个日期之后
        boolean after = of.isAfter(now1);
        System.out.println(after);

        //判断这两个日期是否相等
        boolean after1 = now.equals(of);
        System.out.println(after1);

        //判断闰年
        boolean leapYear = of.isLeapYear();
        System.out.println(leapYear);

        System.out.println("================解析===========================");

        //给出一个符合默认格式要求的日期字符串
        dateStr = "2020-01-01";

        //把日期字符串解析成日期对象 如果日期字符串时年月日 解析时用  LocalDate
        LocalDate parse = LocalDate.parse(dateStr);
        System.out.println(parse);

        System.out.println("===========================================");
        //给出一个符合默认格式要求的 时分秒 字符串
        String dateTimeStr = "14:20:30";

        //把 时分秒 字符串解析成时分秒对象
        LocalTime parse1 = LocalTime.parse(dateTimeStr);
        System.out.println(parse1);

        System.out.println("=========================================");
        //给出一个符合默认格式要求的 日期时分秒 字符串
        String str = "2018-12-12T14:20:30";

        //把 日期时分秒 字符串解析成时分秒对象
        LocalDateTime parse2 = LocalDateTime.parse(str);
        System.out.println(parse2);

        System.out.println("========================================");
        //给出一个自定义日期时分秒格式字符串
        String dateStr2 = "2020年12月12日 12:13:14";

        //给出一个自定义解析格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

        //按照指定的格式去解析
        LocalDateTime parse3 = LocalDateTime.parse(dateStr2, formatter);
        System.out.println(parse3);

        //可以给当前的日期增加时间量
        LocalDateTime newDate = now.plusYears(1);
        year = newDate.getYear();
        System.out.println(year);

        System.out.println("================================");
        //减去时间量的方法minusXXX 系列的方法 返回的是一个新的日期对象
        now1 = LocalDate.now();
        LocalDate newDate2 = now1.minusDays(10);
        dayOfMonth = newDate2.getDayOfMonth();
        System.out.println(dayOfMonth);


        localDate = now1.withYear(2014);
        System.out.println(localDate);

        // TemporalAdjusters工具类，提供了一些获取特殊日期的方法
        LocalDate with = now1.with(TemporalAdjusters.firstDayOfMonth());
        System.out.println(with);
        LocalDate with1 = now1.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println(with1);

        //获取这个月的第几个星期几是几号,比如 TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY)
        // 代表的意思是这个月的第二个星期五是几号
        LocalDate with2 = now1.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.FRIDAY));
        System.out.println(with2);

        System.out.println("=========自定义日期=====下一个工作日======");
        //自定义日期 —— 下一个工作日
        with2 = now1.with(new TemporalAdjuster() {
            @Override
            //参数 nowDate 当前的日期对象
            public Temporal adjustInto(Temporal nowDate) {
                //向下转型
                LocalDate date = (LocalDate) nowDate;
                if (date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {
                    return date.plusDays(3);
                } else if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
                    return date.plusDays(2);
                } else {
                    return date.plusDays(1);
                }
            }
        });
        System.out.println("下一个工作日是：" + with2);


        System.out.println("========时间戳======");

        //  Instant 时间戳类从1970 -01 - 01 00:00:00 截止到当前时间的毫秒值
        Instant instant = Instant.now();
        System.out.println(instant); //获取的是默认时区，获取的不是中国 的时区

        //获取当前时区的，我们可以添加偏移量,返回偏移过后的日期
        OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);
        System.out.println("===========================");

        //从1970 - 01 - 01 00:00:00 截止到当前时间的毫秒值
        long l = System.currentTimeMillis();
        System.out.println(l);

        //JDK1.8 Instant 时间戳类从1970 -01 - 01 00:00:00 截止到当前时间的毫秒值
        instant = Instant.now();

        //toEpochMilli():从1970 -01 - 01 00:00:00 截止到当前时间间隔的毫秒值
        long l1 = instant.toEpochMilli();
        System.out.println(l1);

        //获取从1970 -01 - 01 00:00:00 截止到当前时间间隔的秒值
        long epochSecond = instant.getEpochSecond();
        System.out.println(epochSecond);

        System.out.println("==========================");
        //给计算机元年增加相应的时间量
        Date date_ = new Date(1000 * 60 * 60 * 24);
        System.out.println(date_);

        //现在 给计算机元年增加相应的时间量
        //5. ofEpochSecond() 方法 给计算机元年增加秒数
        //ofEpochMilli() 给计算机元年增加毫秒数
        Instant instant1 = Instant.ofEpochMilli(1000 * 60 * 60 * 24);
        System.out.println(instant1);

        //ofEpochSecond() 方法 给计算机元年增加秒数
        Instant instant2 = Instant.ofEpochSecond(60 * 60 * 24);
        System.out.println(instant2);

        System.out.println("==========计算时间的间隔==========");

        //计算时间的间隔
        Instant start = Instant.now();
        for (int i = 0; i < 15; i++) {
            System.out.println(i);
        }
        Instant end = Instant.now();
        Duration duration = Duration.between(start, end);
        long ll = duration.toNanos();

        //间隔的时间
        System.out.println("循环耗时：" + ll + "纳秒");

        System.out.println("==========计算两个日期的间隔==========");
        //计算两个日期的间隔
        LocalDate birthday = LocalDate.of(2012, 12, 12);
        LocalDate ld = LocalDate.now();
        //我从出生到现在，有多少岁，零几个月，几天
        //计算两个日期的间隔
        Period between = Period.between(birthday, ld);
        int years = between.getYears();
        int months = between.getMonths();
        int days = between.getDays();
        System.out.println("相隔了" + years + "年" + months + "月" + days + "天了...");


        // 之前格式化日期的类  new SimpleDateFormat()
        //JDK1.8 DateTimeFormatter
        //指定格式 静态方法 ofPattern()
        formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // DateTimeFormatter 自带的格式方法
        now = LocalDateTime.now();
        //把日期对象，格式化成字符串
        String format = formatter.format(now);
        //刚才的方式是使用的日期自带的格式化方法
        String format1 = now.format(formatter);
        System.out.println(format);
        System.out.println(format1);

        System.out.println("=========时区============");
        //ZoneID 世界时区类
        //获取世界各地的时区编号。
        Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        for (String availableZoneId : availableZoneIds) {
            System.out.println(availableZoneId);
        }

        System.out.println("=====================");
        //获取系统的默认时区编号
        ZoneId zoneId = ZoneId.systemDefault();
        System.out.println(zoneId);

        //获取其他国家的日期
        now = LocalDateTime.now();
        //获取指定时区的日期时间
        ZoneId zoneId1 = ZoneId.of("Europe/Monaco");
        ZonedDateTime zonedDateTime = now.atZone(zoneId1);  //获得指定时区的当前时间
        System.out.println(zonedDateTime);

        System.out.println("=====================");
        //根据时区，获取该地区的日期
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("America/Phoenix"));  //获得指定时区的当前时间（不带时区信息）
        System.out.println(ldt);
    }

    public static void main(String[] args) throws InterruptedException {
        TestDateUtil();
    }

}